package com.cskaoyan.javse.object._3equals;

/**
 * @description:_ equals方法
 * @author: wuguidong@cskaoyan.onaliyun.com
 **/

/**
 * equals方法:
 * 1.方法的声明: public boolean equals(Object obj)
 *      需要注意的是两点:
 *          a.该方法是有参数的,需要传入一个对象(任意一个对象)
 *          b.方法是有返回值的,返回一个布尔类型的值,真或假
 *
 * 2.方法的作用:
 *      equals有相同,相等的意思,该方法用于指示其他某个对象是否与此对象“相等”。
 *
 * 3.何为对象相等?
 *      相等也就意味着比较,比较就意味着有规则
 *      a.Object类当中的equals默认实现,认为的对象相等: return (this == obj);
 *          双等号连接引用,比较对象的地址,换句话说是比较两个引用是否指向同一个对象
 *          如果指向同一个对象,方法返回true,否者就会返回false
 *          这个方法只有自己和自己比较时,才会返回true,这是一个非常苛刻的条件
 *          在多数情况下,我们不需要这样一个苛刻的规则,需要自己重写equals方法
 *
 *      b,既然需要重写equals方法,就需要一个重写的规则,我们认为的对象相等的规则:
 *          1.如果不是相同类型的对象,没有可比性,直接认定为不相等
 *          2.如果是相同类型的对象,行为是一致的,只有成员变量的取值有差异,
 *              所以我们认为成员变量取值相同的两个对象是相等的
 *          对于equals方法的重写,Java官方为我们提供了官方的要求,称之为equals方法重写的常规协定
 *          自反性：对于任何非空引用值 x，x.equals(x) 都应返回 true
 *              自己和自己比仍然是相等的
 * -          排他性：当比对的不是同种类型的对象或者是一个null时，默认返回false
 *             不是相同类型或者是null,就直接返回false,可以运用Class对象不同类型是不同的.相同类型是一个对象
 *
 *           对称性：对于任何非空引用值 x 和 y，当且仅当 y.equals(x) 返回 true 时，x.equals(y) 才应返回true
 *      x=y --> y=x
 *
 *           传递性：对于任何非空引用值 x、y 和 z，如果 x.equals(y) 返回 true，并且 y.equals(z) 返回 true,那么x.equals(z) 应返回 true
 *
 *   x=y,y=z --> x=z
 *
 * -           一致性：对于任何非空引用值 x 和 y，多次调用 x.equals(y) 始终返回 true 或始终返回 false
 *   -          前提是对象上equals 比较中所用的信息没有被修改
 *
 *  以上三点,对称性,一致性,传递性,只需要用成员变量的取值来判断对象相等,就自动满足它们
 *
 * 按照以上规则重写equals方法,就完成了我们的比较规则,只有当两个对象成员变量取值完全一致时,它们才是相等的
 *
 * 重写equals方法的注意事项:
 *      1.在实现排他性时,实际上有两种选择:
 *          a.getClass比较两个引用的类型,它是比较苛刻的,只有在完全是同一个类型时才会返回true
 *          b.instanceof
 *          引用 instanceof 类名 表示判断引用所指向的对象的具体类型是否是后面类名的对象或者子类对象,如果是就返回true,如果不是就返回false.如果引用指向null,不管类名写什么都返回false
 *          当使用instanceof判断引用类型时,条件比较宽松,可以允许传入子类对象(当子类对象的父类成员和父类对象相同时,equals方法仍然返回true)
 *
 *      2.equals方法也是可以用快捷键自动生成的,alt+insert
 *
 *      3.浮点数比较特殊,它具有规格化和非规格化的区别,还有非数(NaN),无穷大,无穷小很多特殊的概念.正常情况下,如果仅仅比较数值,用"=="比较相等是够用的
 *      但是仍然建议使用对应包装类型的compare去比较浮点数的大小:
 *      Float.compare(float a,float b)
 *      Double.compare(double a,doublet b)
 *      这两个方法在,a < b时返回-1(负数),在a>b时,返回1(正数),只有在两个浮点数相等时,才会返回0
 *      建议后续比较浮点数的大小,统一用该方法,不要用"=="
 *
 *      4.如果类中有引用数据类型成员变量,需要去调用它们的equals方法完成比较
 *
 *
 *
 **/
public class Demo {
    public static void main(String[] args) {
        Student s1 = new Student(1, 18, 100);
        Student s2 = new Student(1, 18, 100);
        System.out.println(s1.equals(s2)); //true
        System.out.println(s1 == s2);

        UniversityStudent s3 = new UniversityStudent(1, 18, 100);
        System.out.println(s1.equals(s3));
    }
}

class Student {
    int id;
    int age;
    double score;
    String name;

    public Student() {
    }

    public Student(int id, int age, double score) {
        this.id = id;
        this.age = age;
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                ", score=" + score +
                '}';
    }

    //重写equals方法
    /*@Override
    public boolean equals(Object obj) {
        //1.满足自反性,自己和自己比,返回true
        if (this == obj) return true;

        //2.排他性
        //2.1 getClass
        if (obj == null || this.getClass() != obj.getClass()) return false;
        //2.2 instanceof
        //if (!(obj instanceof Student)) return false;

        //如果程序走到这里,意外着obj对象不是null,其次obj引用指向的对象一定是Student类型
        // 3.对称性,一致性,传递性只需要按照成员变量的取值判断对象相等就自动满足
        Student target = (Student) obj;
        if (target.id != this.id) return false;
        if (target.age != this.age) return false;
        //比较浮点数的大小不要用"=="
        //return target.score == this.score;
        return Double.compare(target.score,this.score) == 0;
    }*/

   /*
   //允许接收子类对象作为equals方法的参数
   @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;

        Student student = (Student) o;

        if (id != student.id) return false;
        if (age != student.age) return false;
        return Double.compare(student.score, score) == 0;
    }
*/

   //不允许子类对象作为参数
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        if (id != student.id) return false;
        if (age != student.age) return false;
        if (Double.compare(student.score, score) != 0) return false;
        return name != null ? name.equals(student.name) : student.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = id;
        result = 31 * result + age;
        temp = Double.doubleToLongBits(score);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}

class UniversityStudent extends Student {
    public UniversityStudent() {
    }

    public UniversityStudent(int id, int age, double score) {
        super(id, age, score);
    }
}